<!DOCTYPE HTML>
<html>
<head>
  <script type="text/javascript" src="../closure/base.js"></script>
  <script type="text/javascript" src="../src/storage.js"></script>
  <script type="text/javascript">
  goog.require('goog.testing.jsunit');
  </script>
  <script type="text/javascript">
    function testInitialLengthIsZero() {
      assertEquals(0, new Storage().length);
    }
    
    function testSettingLengthIsNoOpForEmptyStorage() {
      var storage = new Storage();
      storage.length = 1;
      assertEquals(0, storage.length);
    }
    
    function testSettingLengthIsNoOpForNonEmpty() {
      var storage = new Storage();
      storage.setItem('a', 'a');
      storage.length = 2;
      assertEquals(1, storage.length);
    }
    
    function testLengthAfterAddingOneItemIsOne() {
      var storage = new Storage();
      storage.setItem('a', 'a');
      assertEquals(1, storage.length);
    }
    
    function testLengthAfterAddingTwoItemsIsTwo() {
      var storage = new Storage();
      storage.setItem('a', 'a');
      storage.setItem('b', 'b');
      assertEquals(2, storage.length);
    }
    
    function testLengthAfterAddingTwoItemsAndRemovingOneIsOne() {
      var storage = new Storage();
      storage.setItem('a', 'a');
      storage.setItem('b', 'b');
      storage.removeItem('a');
      assertEquals(1, storage.length);
    }
    
    function testLengthAfterAddingTwoItemsAndRemovingTwoIsZero() {
      var storage = new Storage();
      storage.setItem('a', 'a');
      storage.setItem('b', 'b');
      storage.removeItem('a');
      storage.removeItem('b');
      assertEquals(0, storage.length);
    }
    
    function testRemovingFromEmptyStorageDoesNotChangeLength() {
      var storage = new Storage();
      storage.removeItem('a');
      assertEquals(0, storage.length);
    }
    
    function testSettingSameElementTwiceOnlyIncrementsLengthOnce() {
      var storage = new Storage();
      storage.setItem('a', 'a');
      storage.setItem('a', 'b');
      assertEquals(1, storage.length);
    }
    
    function testRemovingNonExistentElementDoesNotDecreaseLength() {
      var storage = new Storage();
      storage.setItem('a', 'a');
      storage.removeItem('b');
      assertEquals(1, storage.length);
    }
    
    function testGettingStringGetsString() {
      var storage = new Storage();
      storage.setItem('a', 'a');
      assertEquals('a', storage.getItem('a'));
    }
    
    function testGettingIntegerGetsString() {
      var storage = new Storage();
      storage.setItem('a', 1);
      assertEquals('1', storage.getItem('a'));
    }
    
    function testGettingArrayGetsCommaJoinedElements() {
      var storage = new Storage();
      storage.setItem('a', [1,2,3]);
      assertEquals('1,2,3', storage.getItem('a'));
    }
    
    function testGettingMixedArrayGetsCommaJoinedElements() {
      var storage = new Storage();
      storage.setItem('a', [1,'asd',{a: 'qwe'}]);
      assertEquals('1,asd,[object Object]', storage.getItem('a'));
    }
    
    function testMakesCopyOfStringWhenAdding() {
      var storage = new Storage();
      var str = 'string';
      storage.setItem('key', str);
      str = 'otherString';
      assertEquals('string', storage.getItem('key'));
    }
    
    function testMakesCopyOfNumberWhenAdding() {
      var storage = new Storage();
      var num = 1;
      storage.setItem('key', num);
      num = 2;
      assertEquals('1', storage.getItem('key'));
    }
    
    function testMakesCopyOfObjectWhenAdding() {
      var storage = new Storage();
      var obj = {a: "asd"};
      storage.setItem('key', obj);
      obj = {b: "qwe"};
      assertEquals('[object Object]', storage.getItem('key'));
    }
    
    function testMakesCopyOfArrayWhenAdding() {
      var storage = new Storage();
      var arr = [1, 'str', {a: "obj"}];
      storage.setItem('key', arr);
      arr = [];
      assertEquals('1,str,[object Object]', storage.getItem('key'));
    }
    
    function testLengthIsZeroAfterClearing() {
      var storage = new Storage();
      storage.setItem('key', 'value');
      storage.clear();
      assertEquals(0, storage.length);
    }
    
    function testGettingAnElementGetsNothingAfterClear() {
      var storage = new Storage();
      storage.setItem('key', 'value');
      storage.clear();
      assertUndefined(storage.getItem('key'));
    }
    
    function testKeyForSingletonReturnsOnlyKey() {
      var storage = new Storage();
      storage.setItem('key1', 'value');
      assertEquals('key1', storage.key(0));
    }
    
    function testKeyReturnsAllKeys() {
      var storage = new Storage();
      storage.setItem('key1', 'value');
      storage.setItem('key2', 'value');
      storage.setItem('key3', 'value');
      var keys = [storage.key(0), storage.key(1), storage.key(2)];
      assertContains('key1', keys);
      assertContains('key2', keys);
      assertContains('key3', keys);
    }
    
    function testOutOfBoundsKeyCallReturnsNull() {
      assertNull(new Storage().key(0));
    }
    
    function testSettingValueWithThrowingFlagShouldThrow() {
      var storage = new Storage();
      storage.nextSetShouldThrow = true;
      try {
        storage.setItem('key', 'value');
        fail('Expected exception');
      } catch (ex) {
        assertEquals(0, ex.indexOf('QUOTA_EXCEEDED_ERROR'))
      }
      assertUndefined(storage.getItem('key'));
    }
    
    function testSettingTwoValuesWithOneThrowingFlagShouldNotThrowOnce() {
      var storage = new Storage();
      storage.nextSetShouldThrow = true;
      try {
        storage.setItem('key', 'value');
      } catch (ex) {
      }
      storage.setItem('key', 'value');
      assertEquals('value', storage.getItem('key'));
    }
    
    function testToStringMatchesChromeToString() {
      assertEquals('[object Storage]', new Storage().toString());
    }
    
    function testDotPropertyDefinedAfterSetting() {
      var storage = new Storage();
      storage.setItem('key1', 'value');
      assertEquals('value', storage.key1);
    }
    
    function testBracketsPropertyDefinedAfterSetting() {
      var storage = new Storage();
      storage.setItem('key1', 'value');
      assertEquals('value', storage['key1']);
    }
    
    function testDotPropertyUndefinedAfterRemoving() {
      var storage = new Storage();
      storage.setItem('key1', 'value');
      storage.removeItem('key1');
      assertUndefined(storage.key1);
    }
    
    function testBracketsPropertyUndefinedAfterRemoving() {
      var storage = new Storage();
      storage.setItem('key1', 'value');
      storage.removeItem('key1');
      assertUndefined(storage['key1']);
    }
    
    function testDotPropertyUndefinedAfterClear() {
      var storage = new Storage();
      storage.setItem('key1', 'value');
      storage.clear();
      assertUndefined(storage.key1);
    }
    
    function testBracketsPropertyUndefinedAfterClear() {
      var storage = new Storage();
      storage.setItem('key1', 'value');
      storage.clear();
      assertUndefined(storage['key1']);
    }
    
    //TODO: Test stringification of keys
    
    function testCanOverrideKeyWithSet() {
      assertCanOverrideWhenSetUsingSetItem('key');
    }
    
    function testCanOverrideKeyWithBracketProperty() {
      assertCanOverrideWhenSetUsingBracketProperty('key');
    }
    
    function assertCanOverrideWhenSetUsingSetItem(property) {
      var storage = new Storage();
      storage.setItem(property, 1);
      assertEquals('1', storage[property]);
      assertIsOverridden(property, storage);
    }
    
    function assertCanOverrideWhenSetUsingBracketProperty(property) {
      var storage = new Storage();
      storage[property] = 1;
      assertEquals(1, storage[property]);
      assertIsOverridden(property, storage);
    }
    
    function assertIsOverridden(property, storage) {
      try {
        if (property === 'key') {
          storage.key(0);
        } else if (property === 'getItem') {
          storage.getItem('foo');
        } else if (property === 'setItem') {
          storage.setItem('foo', 'bar');
        } else if (property === 'removeItem') {
          storage.removeItem('foo');
        } else if (property === 'clear') {
          storage.clear();
        }
        fail('Expected exception');
      } catch (ex) {
        assertTrue(ex instanceof TypeError);
      }
    }
    
    function testCanOverrideGetItemWithSet() {
      assertCanOverrideWhenSetUsingSetItem('getItem');
    }
    
    function testCanOverrideGetItemWithBracketProperty() {
      assertCanOverrideWhenSetUsingBracketProperty('getItem');
    }
    
    function testCanOverrideSetItemWithSet() {
      assertCanOverrideWhenSetUsingSetItem('setItem');
    }
    
    function testCanOverrideSetItemWithBracketProperty() {
      assertCanOverrideWhenSetUsingBracketProperty('getItem');
    }
    
    function testCanOverrideRemoveItemWithSet() {
      assertCanOverrideWhenSetUsingSetItem('removeItem');
    }
    
    function testCanOverrideRemoveItemWithBracketProperty() {
      assertCanOverrideWhenSetUsingBracketProperty('removeItem');
    }
    
    function testCanOverrideClearWithSet() {
      assertCanOverrideWhenSetUsingSetItem('clear');
    }
    
    function testCanOverrideClearWithBracketProperty() {
      assertCanOverrideWhenSetUsingBracketProperty('clear');
    }
    
    /*
    function testOverridingKeyWithSetItemTakesPrecedenceOverPropertySet() {
      var storage = new Storage();
      storage.setItem('key', 1);
      storage.key = 2;
      assertEquals('1', storage.key);
      storage.removeItem('key');
      assertEquals(2, storage.key);
      delete s.key;
      assertFalse(-1, storage.key.toString().indexOf('function'));
    }*/
  </script>
</head>
</html>